home
***
CD-ROM
|
disk
|
FTP
|
other
***
search
/
HPAVC
/
HPAVC CD-ROM.iso
/
STDERRF.ZIP
/
STARTUP1.ASM
< prev
next >
Wrap
Assembly Source File
|
1995-05-29
|
12KB
|
424 lines
page 60,132
title Start_Up1 -- Intermediate Start Up Code for .EXE Programs
name Start_Up1
comment ÷
Start_Up1 V1.05
--------------------------------------------------------------------------
NAME
startup1 Intermediate start up code for .exe programs
SYNOPSIS
extern Start_Up1
or
extern Start_Up1S
See Programming Notes below for difference.
DESCRIPTION
This is the startup code for all .exe and .com assembly language
programs. Just use the SYNOPSIS above in the main function to
include the startup code in the .exe file from a .lib. For .com
assembly language programs, this source code must be the first
assembled so that this code is the linked first.
This procedure parses the command line into argc and *argv[] similar
to C. Argv[0] is the first command line argument not the program
name as in C.
This procedure performs the following functions in addition to above
- Initializes the following global variables:
-- DGRP, segment address of DGROUP
-- STACK_BOTTOM, offset to stack bottom in DGROUP
-- PSP, segment address of PSP
-- ENVIRON, segment address of passed copy of the ENVIRONMENT
-- OSMAJOR, integer part of OS system
-- OSMINOR, decimal part of OS system
- If DOS version is less than 2.0, aborts with error message
- Initializes DS and ES segment registers to DGROUP
- Skrinks memory down to size of program by releasing all memory
above program
RETURNS
If main returns to startup code. The program terminiates with the
return code in AL.
PROGRAMING NOTES
Assembled with Microsoft MASM V6.11A.
Written to link with any memory model
Assemble with the following command line options to get the desired
memory models:
Command Line Memory Model
/Dmemmod=tiny Tiny (.com file)
/Dmemmod=small Small (default)
/Dmemmod=compact Compact
/Dmemmod=medium Medium
/Dmemmod=large Large
/Dmemmod=huge Huge
Written using the FORTRAN/PASCAL/BASIC calling convention so passed
parameters are pushed in the order of appearance in the proc
declaration.
This procedure can pass argc and argv on stack two ways. The proc
declaration in the MAIN procedure will differ depending upon the
choice.
The default requires the following proc declaration:
MAIN proc ARGC:word, ARGV:ptr
If SIMPLE is defined, an alternate proc declaration is required:
MAIN proc ARGV:ptr, ARGC:word
Here, ARGV is not a pointer to an array of pointers but the location
of the 1st pointer.
Procedures called:
Main
DOS Interrupts
Int 21h 9h - Display String
Int 21h 30h - Get Version Number
Int 21h 4ah - Set Memory Block Size
Int 21h 4ch - Terminate program with return code
In Huge memory model, the pointers passed in ARGV are long pointer
vice huge pointers.
MEMORY REQUIREMENTS
(in bytes) Tiny Small Medium Compact Large Huge
Code: 211 215 217 218 220 220
Code (SIMPLE): 208 212 214 214 216 216
_Data: 0 0 0 0 0 0
Const: 14 14 14 14 14 14
_BBS: 10 10 10 10 10 10
Stack: ? ? ? ? ? ?
The first code size is for SIMPLE not defined. The Code (SIMPLE) is
for when SIMPLE is defined.
Stack usage is determined by the size of and the number of arguments
in the command line.
CAUTION
Startup1 defines a 512 byte stack. This should be enough for programs
who make moderate use of the stack. If automatic variables are used
extensively, more stack space should be defined in the main module.
AUTHOR
Raymond Moon - 7 Sep 87
Copyright (c) 1987, 1988, 1994, 1995 - MoonWare
ALL RIGHTS RESERVED
VERSION
Version - Date - Remarks
1.00 - 7 Sep 87 - Original
1.01 - 13 Feb 88 - Updated to program name as argv[0]
- This created Startup1 from Startup0
1.02 - 4 Jul 88 - Converted to MASM V5.1
1.03 - 9 Oct 88 - Added Stack_Bottom & ZZ_PRGM_TOP segment
to determine end of data.
1.04 - 30 Oct 94 - Moved ZZ_PRGM_TOP segment position in file.
- Removed increment in program size in paras
as ZZ_PRGM_TOP is aligned on a para.
1.05 - 5 Mar 95 - Added TINY Model capability
- Converted to .dosseg using __end vice
ZZ_PRGM_TOP for end of data
==========================================================================
÷ Commend End
;-----------------------------
; Make the small memory model the default
ifndef memmod
memmod equ <small>
endif
include procesor.inc
% .MODEL memmod,FORTRAN
assume es:DGROUP
.dosseg
;----------------------------
; Required includes
include stderrf.inc
;----------------------------
; Define any required equates
STACK_SIZE equ 512
;=========================================================================
; DATA
;=========================================================================
; Define storage for the various global and system variables and
; constants.
.CONST
BAD_DOS_VERSION db 'Need DOS 2.0+$'
.DATA?
DGRP dw ? ; Value of DGROUP
STACK_BOTTOM dw ? ; Offset to stack bottom in DGROUP
PSP dw ? ; Segment address of PSP
ENVIRON dw ? ; Segment address of ENVIRON
OSMAJOR db ? ; Integer part of OS system
OSMINOR db ? ; Decimal part of OS system
if @Model NE 1
.STACK STACK_SIZE ; Define a nominal stack
endif
@CurSeg ends
;----------------------------
; Define __end which is defined when using .dosseg
externdef __end:byte
;----------------------------
; Define segment for addressing information in the PSP
PSP_SEG segment at 00h
org 2ch
ENVIRON_PTR dw ? ; Segment address of Environment
org 80h
PARM_LEN db ? ; Number of bytes in Command Line tail
PARMS db 127 dup(?) ; Start of Command Line tail
PSP_SEG ends
;=========================================================================
; CODE
;=========================================================================
; Put the called main procedure in the proper relationship to the
; startup code.
if @CodeSize
extrn Main:far
.CODE
else
.CODE
extrn Main:near
endif
;-----------------------------
; Include org statement if tiny model
if @Model EQ 1
org 100h
endif
;-----------------------------
; Start the Start_Up1 code. Make it a far procedure so error return
; will work. If SIMPLE is defined, redefined Start_Up1 as Start_Up1S.
ifdef SIMPLE
Start_Up1 equ <Start_Up1S>
endif
% Start_Up1 proc far
;-----------------------------
; First, initialize global variables. Set DS to DGROUP.
if @Model NE 1
mov ax, DGROUP ; Get seg address of DGROUP
mov ds, ax ; Initialize DS segment register
mov DGRP, ax ; Initialize DGRP
else
mov DGRP, ds ; Initialize DGRP
endif
mov PSP, es ; Initialize PSP
assume es:PSP_SEG
mov bx, es:ENVIRON_PTR ; Get segment address of environment
mov ENVIRON, bx ; Initialize ENVIRON
mov ah, 30h ; Get DOS version number
int 21h ; Call DOS
mov OSMAJOR, al ; Save major version number
mov OSMINOR, ah ; Save minor version number
;----------------------------
; If DOS version is prior to 2.0, write error message and terminate
; the program. The program terminates with a far call to INT 20h.
cmp al, 2 ; Is the OS 2.0 below?
jae SU1 ; No, continue
mov ah, 9 ; DOS display string
lea dx, BAD_DOS_VERSION ; DS:DX => string to be displayed
int 21h ; Call DOS
push PSP ; Push return segment
xor ax, ax ; AX = 0
push ax ; Push return IP
ret ; Far return to PSP:0000
;----------------------------
; Combine the stack into DGROUP so that it is addressable from DGROUP.
; Initialize STACK_BOTTOM.
SU1: lea bx, __end ; Get pointer to end of data
if @Model eq 1 ; Ensure that Stack bottom at paragraph
and bx, 0fff0h ; Truncate to lower paragraph
add bx, 16 ; Add one paragraph
endif
mov STACK_BOTTOM, bx ; Save it
if @Model eq 1 ; Get stack size
add bx, STACK_SIZE ; DI = stack size
else
add bx, sp ; DI = stack size
endif
mov dx, ds ; Get DGROUP segment address
mov ss, dx ; Reset SS
mov sp, bx ; Reset SP
;----------------------------
; Release all memory above program. Calculate the size of the program
; in paragraphs (16 bits). BX starts with Stack Top
mov cl, 4 ; Convert to #para by dividing by 16
shr bx, cl ; Do division by bit shifting
if @Model ne 1 ; Needed in non-Tiny memory models
mov ax, ds ; AX => DGROUP
sub ax, PSP ; AX = # para for code
add bx, ax ; BX = # para in program
endif
mov ah, 4ah ; Request DOS set block
int 21h ; Call DOS
;----------------------------
; See if there are any command line arguments. If not, all command
; line processing is skipped. The pushed null on the stack will be
; argc, and a null pointer is also pushed onto the stack so argv[0]
; is a null pointer.
xor dx,dx ; Ensure DX is null
push dx ; Ensure null on top of stack
mov bp,sp ; BP = top of stack
cmp es:PARM_LEN,0 ; Are there any Command Line arguments
jne SU2 ; No, go process what is on the stack
push dx ; Push null pointer
if @DataSize
push dx ; Make a doubleword null pointer
endif
mov es,DGRP ; Initialize ES
jmp short SU10 ; Go call MAIN
;----------------------------
; There are command line arguments. Parse them onto the stack and
; build ARGC and ARGV. Start this by transferring the entire command
; line tail onto the stack. Make room for them by moving SP.
SU2: mov cl, es:PARM_LEN ; Get # of bytes in Command Line
inc cx ; increase by one
and cx, 0feh ; Force an even count
mov ax, sp ; Get SP
sub ax, cx ; Subtract PARM_LEN
mov sp, ax ; Reset SP, room on Stack
lea si, es:PARMS ; Load source addr in SI
mov di, sp ; Load destin addr in DI
mov es, DGRP ; ES => DGROUP
assume es:DGROUP
mov ds, PSP ; DS => PSP
rep movsb ; Move Command Line onto the Stack
mov ds, es:DGRP ; Restore DS
;-----------------------------
; Convert all blanks, not within double quotes, in the Command Line
; to Nul. CX is IN_LITERAL_FLAG.
mov bx, bp ; BX points to last byte in stack
xor cx, cx ; Clear IN_LITERAL_FLAG
SU3: mov al, [bx] ; Get byte
cmp al, '"' ; Is it a literal?
jne SU4 ; No, go to next test
inc cx ; Set IN_LITERAL_FLAG
and cx, 1 ; Ensure only 0, & 1 valid
jmp short SU5 ; Continue, and blank '"'
SU4: cmp al, ' ' ; Is it a blank?
ja SU6 ; No, go set up to get another
or cx, cx ; Is IN_LITERAL_FLAG clear?
jne SU6 ; No, do not null blank
SU5: xor al, al ; Nul AX
mov [bx], al ; Store Nul in [BX]
SU6: dec bx ; BX point to next byte
cmp bx, sp ; Are we through yet?
jnb SU3 ; No, go one mo' 'gin
;-----------------------------
; Build *argv[]. argc kept in CX. DX used as IN_WORD flag
; Build it backwards. CX is 0 upon entry.
mov dx, cx ; Set CX (argc) to 0
push cx ; Put a null at the start
mov bx, bp ; BX point to last byte
mov bp, sp ; BP now points to Top of Stack
SU7: mov al, [bx - 1] ; Get byte
or al, al ; Is it Nul?
jnz SU8 ; No, it is a char
or dx, dx ; Was the last byte not a char?
jz SU9 ; Yes, go on with the processing
xor dx, dx ; No, it was a char. Clear IN_WORD.
inc cx ; Increment argc
if @DataSize
push ss ; Push segment address
endif
push bx ; Push addr onto stack
jmp short SU9 ; Go set up for another byte
SU8: inc dx ; Set DX to IN_WORD
SU9: dec bx ; BX point to next byte
cmp bx, bp ; Are we at the 1st byte yet?
jg SU7 ; No, go process another
;-----------------------------
; Create argc and argv on the stack. Default is FORTRAN calling
; convention. If SIMPLE is defined, push only argc. See documentation
; above for structure of proc declaration in MAIN procedure
ifndef SIMPLE
mov bx, sp ; BX = **argv[]
push cx ; Push argc
if @DataSize
push ss ; Long pointer
endif
push bx ; Push **argv[]
else
push cx ; Push argc
endif
;-----------------------------
; call MAIN
SU10: call Main ; Call MAIN procedure
;----------------------------
; If main returns, the program is to terminate with the return code
; returned in AL
mov ah, 4ch ; End process
int 21h ; Call DOS
% Start_Up1 endp
% end Start_Up1 ; Indicate that Start_Up1 is the start
; of the program